﻿// <copyright file="IndexHeader.cs" company="Sinclair Community College">
// Copyright 2010 Sinclair Community College
// </copyright>
// <originalAuthor>Patrick S. Seymour (patrick.seymour@gmail.com)</originalAuthor>

namespace SinclairCC.AppV
{
    using System;
    using System.Diagnostics.CodeAnalysis;

    /// <summary>
    /// This class represents the Index Header section in an App-V SFT/DSFT file.
    /// </summary>
    /// <remarks>The Index Header section stores information about the location of various
    /// other sections in the SFT file (offsetting information) and the overall sizes
    /// of the data payload.</remarks>
    public class IndexHeader : HeaderMetadataSection
    {
        /// <summary>
        /// The stream identifier.
        /// </summary>
        /// <remarks>
        /// This field is initialized to all zeroes. Older SFT files may contain
        /// non-zero data in this field.
        /// </remarks>
        private int streamId;

        /// <summary>
        /// A unique ID case-insensitive value (GUID).
        /// </summary>
        private Guid packageId;

        /// <summary>
        /// The internal version number for the directory map.
        /// </summary>
        /// <remarks>
        /// This value starts initially from one for the newly created package
        /// and then increments by one per each additional resave of that same package,
        /// following the package’s version number.
        /// </remarks>
        private int directoryMapVersion;

        /// <summary>
        /// The total size, in bytes, of the SECD entries in the compressed index (CIDX) section.
        /// </summary>
        private int secdMapSize;

        /// <summary>
        /// The total size, in bytes, of the directory map subsection in the compressed index
        /// (CIDX) section.
        /// </summary>
        private int directoryMapSize;

        /// <summary>
        /// The total size, in bytes, of all the files that are stored inside data blocks (DATA).
        /// </summary>
        /// <remarks>This value does not include the DATA section headers.</remarks>
        private int totalFileDataSize;

        /// <summary>
        /// The total size, in bytes, of all the block map entries in the BMAP section.
        /// </summary>
        /// <remarks>This value does not include the BMAP section header.</remarks>
        private int blockMapSize;

        /// <summary>
        /// The total length, in bytes, of all the FB1 DATA block payloads.
        /// </summary>
        private long featureBlockOneDataSize;

        /// <summary>
        /// This field is reserved and is initialized to all zeroes.
        /// </summary>
        private byte[] secdOffset;

        /// <summary>
        /// The absolute byte offset inside the SFT file to the beginning of the directory
        /// map section (CIDX).
        /// </summary>
        private long directoryMapOffset;

        /// <summary>
        /// The byte offset to the beginning of the FB1 data blocks relative to the start
        /// of the SFT file.
        /// </summary>
        private long featureBlockOneOffset;

        /// <summary>
        /// The absolute byte offset inside the SFT file to the beginning of the block map
        /// header section (BMAP).
        /// </summary>
        private long blockMapOffset;

        /// <summary>
        /// The original creation timestamp for the directory map.
        /// </summary>
        /// <remarks>
        /// In the SFT file, this value is stored as a Win32 FILETIME structure.
        /// The format is the UTC (GMT) time zone.
        /// </remarks>
        private DateTime directoryMapCreationTimestamp;

        /// <summary>
        /// The last modification timestamp for the directory map.
        /// </summary>
        /// <remarks>In the SFT file, this value is stored as a Win32 FILETIME structure.
        /// The format is the UTC (GMT) time zone.</remarks>
        private DateTime directoryMapModificationTimestamp;

        /// <summary>
        /// Initializes a new instance of the IndexHeader class.
        /// </summary>
        /// <param name="fileReader">
        /// A binary file reader that points to the SFT file.
        /// </param>
        public IndexHeader(System.IO.BinaryReader fileReader)
            : base(fileReader)
        {
        }
        
        /// <summary>
        /// Gets the stream identifier.
        /// </summary>
        /// <remarks>This field is initialized to all zeroes. Older SFT files may contain
        /// nonzero data in this field.</remarks>
        public int StreamIdentifier
        {
            get
            {
                this.ReadSection();
                return this.streamId;
            }
        }

        /// <summary>
        /// Gets the unique identifier for this package.
        /// </summary>
        public Guid PackageId
        {
            get
            {
                this.ReadSection();
                return this.packageId;
            }
        }

        /// <summary>
        /// Gets the internal version number for the directory map.
        /// </summary>
        /// <remarks>This value starts initially from one for the newly created package
        /// and then increments by one per each additional resave of that same package,
        /// following the package’s version number.</remarks>
        public int DirectoryMapVersion
        {
            get
            {
                this.ReadSection();
                return this.directoryMapVersion;
            }
        }

        /// <summary>
        /// Gets the total size, in bytes, of the SECD entries in the compressed index (CIDX) section.
        /// </summary>
        public int SecdMapSize
        {
            get
            {
                this.ReadSection();
                return this.secdMapSize;
            }
        }

        /// <summary>
        /// Gets the total size, in bytes, of the directory map subsection in the
        /// compressed index (CIDX) section.
        /// </summary>
        public int DirectoryMapSize
        {
            get
            {
                this.ReadSection();
                return this.directoryMapSize;
            }
        }

        /// <summary>
        /// Gets the total size, in bytes, of all the files that are stored inside data blocks (DATA).
        /// </summary>
        /// <remarks>This value does not include the DATA section headers.</remarks>
        public int TotalFileDataSize
        {
            get
            {
                this.ReadSection();
                return this.totalFileDataSize;
            }
        }

        /// <summary>
        /// Gets the total size, in bytes, of all the block map entries in the BMAP section.
        /// </summary>
        /// <remarks>This value does not include the BMAP section header.</remarks>
        public int BlockMapSize
        {
            get
            {
                this.ReadSection();
                return this.blockMapSize;
            }
        }

        /// <summary>
        /// Gets the total length, in bytes, of all the FB1 DATA block payloads.
        /// </summary>
        public long FeatureBlockOneDataSize
        {
            get
            {
                this.ReadSection();
                return this.featureBlockOneDataSize;
            }
        }

        /// <summary>
        /// Gets the absolute byte offset inside the SFT file to the beginning of the directory
        /// map section (CIDX).
        /// </summary>
        public long DirectoryMapOffset
        {
            get
            {
                this.ReadSection();
                return this.directoryMapOffset;
            }
        }

        /// <summary>
        /// Gets the byte offset to the beginning of the FB1 data blocks relative to the start
        /// of the SFT file.
        /// </summary>
        public long FeatureBlockOneOffset
        {
            get
            {
                this.ReadSection();
                return this.featureBlockOneOffset;
            }
        }

        /// <summary>
        /// Gets the absolute byte offset inside the SFT file to the beginning of the block map
        /// header section (BMAP).
        /// </summary>
        public long BlockMapOffset
        {
            get
            {
                this.ReadSection();
                return this.blockMapOffset;
            }
        }

        /// <summary>
        /// Gets the original creation timestamp for the directory map.
        /// </summary>
        /// <remarks>
        /// The format is the UTC (GMT) time zone.
        /// </remarks>
        public DateTime DirectoryMapCreated
        {
            get
            {
                this.ReadSection();
                return this.directoryMapCreationTimestamp;
            }
        }

        /// <summary>
        /// Gets the last modification timestamp for the directory map.
        /// </summary>
        /// <remarks>
        /// The format is the UTC (GMT) time zone.
        /// </remarks>
        public DateTime DirectoryMapModified
        {
            get
            {
                this.ReadSection();
                return this.directoryMapModificationTimestamp;
            }
        }

        /// <summary>
        /// Reads the actual data for this section from the file.
        /// </summary>
        protected override void ReadSection()
        {
            if (this.SectionHasBeenRead)
            {
                return;
            }

            // Seek to the start of the data area for this section.
            long currentPosition = FileReader.BaseStream.Position;
            FileReader.BaseStream.Seek(this.StartPositionInFile + CommonSectionHeader.HeaderSize, System.IO.SeekOrigin.Begin);

            // Read the properties from the file.
            this.streamId = FileReader.ReadInt32();

            byte[] guidBytes = FileReader.ReadBytes(16);
            this.packageId = new Guid(guidBytes);

            this.directoryMapVersion = FileReader.ReadInt32();
            this.secdMapSize = FileReader.ReadInt32();
            this.directoryMapSize = FileReader.ReadInt32();
            this.totalFileDataSize = FileReader.ReadInt32();
            this.blockMapSize = FileReader.ReadInt32();
            this.featureBlockOneDataSize = FileReader.ReadInt64();
            this.secdOffset = FileReader.ReadBytes(8);
            this.directoryMapOffset = FileReader.ReadInt64();
            this.featureBlockOneOffset = FileReader.ReadInt64();
            this.blockMapOffset = FileReader.ReadInt64();
            this.directoryMapCreationTimestamp = DateTime.FromFileTimeUtc(FileReader.ReadInt64());
            this.directoryMapModificationTimestamp = DateTime.FromFileTimeUtc(FileReader.ReadInt64());

            this.SectionHasBeenRead = true;
            FileReader.BaseStream.Seek(currentPosition, System.IO.SeekOrigin.Begin);
        }
    }
}
